import qs from "qs"
import * as auth from 'auth-provider'
import { useAuth } from "context/auth-context"

const apiURL = process.env.REACT_APP_API_URL

interface Config extends RequestInit {
  data?: object,
  token?: string
}
// endpoint为url中末尾的主要参数
// 目前这个http函数还需要手动将jwt token传入
// 当一个参数有默认值时，他自动变为可选参数
export const http = async (endpoint:string, {data, token, headers, ...customConfig}:Config = {}) => {
  const config = {
    // 这里只是method默认为GET，customConfig后续会覆盖
    method: 'GET',
    headers: {
      Authorization: token ? `Bearer ${token}` : '',
      'Content-Type': data ? 'application/json' : '',
    },
    ...customConfig
  }
  if (config.method.toUpperCase() === 'GET'){
    endpoint += `?${qs.stringify(data)}`
  }else {
    config.body = JSON.stringify(data || {})
  }

  // axios和fetch的表现不一样，axios可以直接在返回状态部位2xx的时候抛出异常
  return window.fetch(`${apiURL}/${endpoint}`, config).then(
    async res => {
      // 未登录或token失效的情况下
      if(res.status === 401) {
        // 触发退出登录，返回登录界面
        await auth.logout()
        // 刷新页面
        window.location.reload()
        return Promise.reject({message: '请重新登录'})
      }
      const data = await res.json()
      if(res.ok) {
        return data
      }else {
        // 服务端返回任何异常fetch API都不会抛出，在断网的时候会抛出
        // 因此为了捕获服务端异常，最好手动抛出异常
        return Promise.reject(data)
      }
    }
  )
}

// 自动携带jwt Token的方法
export const useHttp = () => {
  const {user} = useAuth()
  // Parameters<typeof http>在此处应用是由于当前传入参数类型与http相同，
  // Utility(utils) Types用法：用泛型给他传入一个其他类型， 然后utility type对这个类型进行某种操作
  return (...[endpoint,config]: Parameters<typeof http>) => http(endpoint, {...config, token: user?.token})
}